
import { GameManager, IUpdatable, regGameManager } from '../GameManager';
import { BaseView, regView } from '../ui/base/BaseView';
import { UIRoot } from '../ui/base/UIRoot';
import { LoggerManager } from './LoggerManager';


/**全屏转圈等待阻挡 */
export const waitingTouchBlockPrefabPath = 'base/ui/WaitingTouchBlock'
/**界面下面的黑背景 */
export const blackBgPrefabPath = 'base/ui/BlackBg'
/**ui根节点 */
export const uiRootPrefabPath = 'base/ui/UIRoot'



/**打开界面参数 */
export class OpenViewOptions {
    /**perfeb路径 */
    path?: any
    /**自定义传给界面的参数 */
    prop?: any
    /**打开完成回调 */
    finishedCbk?: Function
    /**加载进度回调 */
    progressCbk?: Function
    /**禁用动画 */
    disableAnim?: boolean
    /**强制隐藏黑背景 */
    forceHideBlackBg?: boolean
    /**关闭完成回调 */
    closeCbk?: (view: BaseView) => void
    /**等待之前的打开任务都完成再打开 */
    waitPreOpenTask?: boolean
    /**bundle名 */
    bundleName?: string
}

/**关闭界面参数 */
export interface CloseViewOptions {

    /**关闭完成时的回调 */
    closeCbk?: (view: BaseView) => void,
    /**禁用动画 */
    disableAnim?: boolean
}

/**界面打开任务，在界面加载过程中存在 */
export class OpenViewTask {
    constructor(
        public id: number,
        public prefabPath: string,
        public options: OpenViewOptions
    ) { }
}

/**ui管理器，主要用来管理不同业务模块视图、弹窗 */
@regGameManager({ initOrder: 1 })
export class UIManager extends GameManager implements IUpdatable {

    static viewClzArr = []

    uiViewClzMap: Map<number, typeof BaseView> = new Map()

    /**已打开的界面列表 */
    viewArr: Array<BaseView> = null

    /**UI界面根节点 */
    uiRoot: UIRoot = null

    /**View界面根节点 */
    viewRoot: cc.Node = null

    /**tip根节点 */
    tipRoot: cc.Node = null

    /**屏蔽界面点击层节点 */
    blockNode: cc.Node = null

    /**等待消息的屏蔽界面点击层节点 */
    waittingBlockNode: cc.Node = null

    /**历史打开过的视图数量 */
    openViewCount = 0

    /**当前打开视图任务集合 */
    openViewTaskMap: Map<number, OpenViewTask> = new Map()

    /**tip根节点预制 */
    uiRootPrefab: cc.Prefab = null

    /**当前打开中的界面数 */
    get openingCount(): number {
        return this.openViewTaskMap.size
    }

    /**canvas节点 */
    canvasNode: cc.Node = null
    /**canvas的区域 */
    canvasAABB: cc.Rect = null

    /**黑背景预制 */
    blackBgPrefab: cc.Prefab = null


    /**初始化 */
    public async initialize(): Promise<void> {
        this.canvasNode = cc.find('Canvas')
        this.uiRoot = this.canvasNode.getComponentInChildren(UIRoot)
        this.viewRoot = this.uiRoot.viewRoot
        this.tipRoot = this.uiRoot.tipRoot
        this.blockNode = this.uiRoot.blockNode
        this.blockNode.active = false
        this.waittingBlockNode = this.uiRoot.waittingBlockNode
        this.waittingBlockNode.active = false
        this.blackBgPrefab = this.uiRoot.blackBgPrefab

        //获取canvas的区域
        this.canvasAABB = this.canvasNode.getBoundingBoxToWorld()

        this.viewArr = []
        this.registerViews()
    }

    /**注册视图，注册到uiManager的视图可以直接用id打开 */
    registerViews() {
        const viewClzArr: any[] = (regView as any).clzArr
        if (viewClzArr) {
            for (let index = 0; index < viewClzArr.length; index++) {
                const clz = viewClzArr[index];
                this.registerView(clz)
            }
        }

    }

    /**注册未填写viewId的视图，自动生成的自增id */
    private autoRegisterSequenceId = 100000

    /**注册视图，方便UIManager后续查找 */
    registerView<T extends (typeof BaseView)>(view: T) {
        if (!view.uiViewConfig.viewId) {
            view.uiViewConfig.viewId = this.autoRegisterSequenceId++
        }
        this.uiViewClzMap.set(view.uiViewConfig.viewId, view)
    }


    /**添加一个视图开启任务 */
    private addOpenTask(prefabPath, options: OpenViewOptions) {
        const task = new OpenViewTask(++this.openViewCount, prefabPath, options)
        this.openViewTaskMap.set(task.id, task)
        this.turnOffScreenClick()
        return task
    }

    /**完成视图开启任务 */
    private finishOpenTask(task: OpenViewTask) {
        this.openViewTaskMap.delete(task.id)
        if (this.openViewTaskMap.size == 0) {
            this.turnOnScreenClick()
        }
    }

    /**等待之前的打开任务都完成 */
    waitPreOpenTasks(taskIds: number[]) {
        for (let index = 0; index < taskIds.length; index++) {
            const taskId = taskIds[index];
            if (this.openViewTaskMap.get(taskId)) {
                return false
            }
        }
        return true
    }

    /**转换viewClzOrViewId为viewClz */
    viewClzOrViewIdToViewClz(viewClzOrViewId: (new () => BaseView) | number) {
        let viewClz: typeof BaseView
        if (typeof viewClzOrViewId == 'number') {
            viewClz = this.uiViewClzMap.get(viewClzOrViewId)
            if (!viewClz) {
                throw new Error(`尝试打开未注册的视图id：${viewClzOrViewId}`);
            }

        } else {
            viewClz = viewClzOrViewId as unknown as typeof BaseView
        }
        return viewClz
    }


    /**
     * 打开界面
     * @param viewClzOrViewId 界面类或者界面id
     * @param options 
     * @returns 
     */
    async openView(viewClzOrViewIdOrPath: (new () => BaseView) | number | string, options: OpenViewOptions = {}): Promise<BaseView> {
        LoggerManager.instance().log("UiManager.openView()", options);

        let viewBundleName = options.bundleName
        let viewPrefabPath = null
        if (typeof viewClzOrViewIdOrPath == 'string') {
            viewPrefabPath = viewClzOrViewIdOrPath
        } else {
            let viewClz = this.viewClzOrViewIdToViewClz(viewClzOrViewIdOrPath)
            viewPrefabPath = viewClz.uiViewConfig.prefabPath
            if (!viewBundleName) {
                viewBundleName = viewClz.uiViewConfig.bundle
            }
        }


        let task: OpenViewTask
        /**等待之前界面打开 */
        if (options.waitPreOpenTask) {
            const preTasks = []

            this.openViewTaskMap.forEach((value, key) => {
                preTasks.push(value.id)
            })

            task = this.addOpenTask(viewPrefabPath, options)
            while (!this.waitPreOpenTasks(preTasks)) {
                await new Promise<void>((resolve) => {
                    setTimeout(() => {
                        resolve()
                    }, 0);
                })
            }
        } else {
            task = this.addOpenTask(viewPrefabPath, options)
        }

        /**历史界面暂停 */
        for (let oldView of this.viewArr) {
            if (!oldView.isPaused) {
                oldView.pause()
            }
        }

        const viewBundle = await this.getBundle(viewBundleName)
        this.increaseWaitingCount()
        let result = null
        let leftRetryCount = 3
        while (leftRetryCount > 0 && !result) {
            leftRetryCount--
            result = await this.loadAndOpenView(task, viewBundle)
        }
        this.decreaseWaitingCount()
        if (!result) {
            window.WebUtils&&WebUtils.showToast('活动太火爆了哦～')
        }
        return result
    }

    async getBundle(bundleName: string) {
        if (!bundleName) {
            return cc.resources
        }

        let bundle = cc.assetManager.getBundle(bundleName)
        if (!bundle) {
            bundle = await this.loadBundle(bundleName)
        }

        return bundle
    }

    loadBundle(bundleName: string): Promise<cc.AssetManager.Bundle | null> {
        console.log("UiManager.loadBundle()", bundleName);

        return new Promise((resolve) => {
            const cdnHost = window["cdn_host"];
            const url = (cdnHost ? cdnHost + "assets/" : "") + bundleName
            cc.assetManager.loadBundle(url, (err, bundle) => {
                if (err) {
                    resolve(null)
                    return
                }
                resolve(bundle)
            })
        })
    }

    /**加载界面预制体后打开（openView） */
    private loadAndOpenView(task: OpenViewTask, viewBundle: cc.AssetManager.Bundle): Promise<BaseView> {
        return new Promise(async (resolve, reject) => {
            const options = task.options
            let path = options.path
            if (!path) path = task.prefabPath
            viewBundle.load(path, cc.Prefab, (completedCount: number, totalCount: number, item: any) => {
                if (options.progressCbk) options.progressCbk(completedCount, totalCount)
            }, async (err, prefab: cc.Prefab) => {
                if (!this.openViewTaskMap.get(task.id)) {
                    resolve(null)
                    return
                }
                if (err) {
                    LoggerManager.instance().error(`err on open View:,:${path}`)
                    LoggerManager.instance().error(err);
                    this.finishOpenTask(task)
                    resolve(null)
                    return
                }
                let viewNode: cc.Node = cc.instantiate(prefab)
                let viewComp = viewNode.getComponent(BaseView)

                if (!viewComp) {
                    LoggerManager.instance().error(`预制中没有添加视图脚本`)
                    resolve(null)
                }


                if (options.closeCbk) {
                    viewComp._closeCbks.push(options.closeCbk)
                }
                viewComp.init(options.prop)

                /**如果打开的界面是一个场景，历史界面进入后台 */
                if (viewComp.hidePreViews) {
                    for (let oldView of this.viewArr) {
                        if (!oldView.isHide) {
                            oldView.hide()
                        }
                    }
                }

                /**黑背景，如果界面在界面加载时显示 */
                let blackBgNode: cc.Node = null
                if (err) {
                    LoggerManager.instance().error(err);
                }
                else if (viewComp.isShowBlackBg && !options.forceHideBlackBg) {
                    blackBgNode = cc.instantiate(this.blackBgPrefab)
                    blackBgNode.opacity = viewComp.blackBgOpacity;
                    this.viewRoot.addChild(blackBgNode)
                    blackBgNode.active = true
                }
                viewComp._blackBg = blackBgNode



                this.viewRoot.addChild(viewNode)
                this.viewArr.push(viewComp)
                viewComp.onOpen()
                LoggerManager.instance().log(viewComp.name + '.onOpen() finish');
                if (options.finishedCbk) {
                    options.finishedCbk()
                }
                /**当完成打开且入场动画播完 */
                const onFinishOpen = () => {
                    if (viewComp.isShowBlackBg && !options.forceHideBlackBg) {
                        viewComp.showBlackBg(true)
                    } else {
                        viewComp.showBlackBg(false)
                    }
                    this.finishOpenTask(task)
                }
                if (!viewComp.disableAnim) {
                    viewComp.playOpenAnim(onFinishOpen)
                } else {
                    onFinishOpen();
                }
                resolve(viewComp)
            })

        })
    }

    /**
     * 关闭界面
     * @param viewOrViewClzOrViewId 视图实体或视图类或视图id
     * @param options 
     */
    closeView(viewOrViewClzOrViewId: BaseView | (new () => BaseView) | number, options: CloseViewOptions = {}) {

        const isBaseViewInstanse = viewOrViewClzOrViewId instanceof BaseView
        const viewClass = isBaseViewInstanse ? viewOrViewClzOrViewId.constructor : this.viewClzOrViewIdToViewClz(viewOrViewClzOrViewId)


        /**移除要关闭的界面 */
        for (let i = this.viewArr.length - 1; i >= 0; i--) {
            let mView = this.viewArr[i]
            if (mView instanceof viewClass && (isBaseViewInstanse ? mView.uuid == viewOrViewClzOrViewId.uuid : true)) {
                const isTopView = i == this.viewArr.length - 1
                if (options.closeCbk) {
                    mView.addCloseCbk(options.closeCbk)
                }
                this.removeView(i, mView, options.disableAnim)
                isTopView && this.resumePreView()
                break
            }
        }
    }

    /**
     * 关闭指定view上层的所有视图
     * @param options 要关闭的视图选项
     * @param contain 是否包含options里的view
     */
    closeToView(viewOrViewClzOrViewId: BaseView | (new () => BaseView) | number, options: CloseViewOptions = {}, contain = false) {
        const isBaseViewInstanse = viewOrViewClzOrViewId instanceof BaseView
        const viewClass = isBaseViewInstanse ? viewOrViewClzOrViewId.constructor : this.viewClzOrViewIdToViewClz(viewOrViewClzOrViewId)

        if (!this.isViewOpen(viewClass as any)) {
            /**目标视图没有打开 */
            return
        }

        for (let i = this.viewArr.length - 1; i >= 0; i--) {
            const mView = this.viewArr[i];
            const isTopView = i == this.viewArr.length - 1
            if (mView instanceof viewClass && (isBaseViewInstanse ? mView.uuid == viewOrViewClzOrViewId.uuid : true)) {
                /**包含的话也一起关 */
                if (contain) {
                    this.removeView(i, mView, options.disableAnim)
                    isTopView && mView.hidePreViews && this.resumePreView()
                }
                break
            } else {
                this.removeView(i, mView, options.disableAnim)
                isTopView && mView.hidePreViews && this.resumePreView()
            }
        }
    }

    /**关闭所有界面，排除指定界面 
     * @param excludeViewOrViewClzOrViewIdArr 要排除的界面列表
    */
    closeAllView(excludeViewOrViewClzOrViewIdArr: (BaseView | (new () => BaseView) | number)[] = [], options: CloseViewOptions = {}) {

        for (let i = this.viewArr.length - 1; i >= 0; i--) {
            const mView = this.viewArr[i];
            const isTopView = i == this.viewArr.length - 1
            const isExclude = excludeViewOrViewClzOrViewIdArr.some((viewOrViewClzOrViewId) => {
                const isBaseViewInstanse = viewOrViewClzOrViewId instanceof BaseView
                const viewClass = isBaseViewInstanse ? viewOrViewClzOrViewId.constructor : this.viewClzOrViewIdToViewClz(viewOrViewClzOrViewId)
                return mView instanceof viewClass && (isBaseViewInstanse ? mView.uuid == viewOrViewClzOrViewId.uuid : true)
            })
            if (!isExclude) {

                this.removeView(i, mView, options.disableAnim)
                isTopView && mView.hidePreViews && this.resumePreView()
            }
        }
    }


    /**
     * 界面上面没有其他界面，包括正在打开的界面
     * @param view 
     * @returns 
     */
    isTopView(view: any) {
        if (this.openingCount) {
            return false
        }
        for (let i = this.viewArr.length - 1; i >= 0; i--) {
            let mView = this.viewArr[i]
            if (mView instanceof view) {
                const isTopView = i == this.viewArr.length - 1
                if (isTopView) {
                    return true
                }
            }
        }
        return false
    }

    /**界面已经打开 */
    isViewOpen(viewClz: new () => BaseView) {
        for (let i = this.viewArr.length - 1; i >= 0; i--) {
            let mView = this.viewArr[i]
            if (mView instanceof viewClz) {
                return true;
            }
        }
        return false
    }

    /**获得一个已打开的界面 */
    getView<T extends BaseView>(viewClzOrViewId: (new () => T) | number): T {
        let viewClz = this.viewClzOrViewIdToViewClz(viewClzOrViewId)
        if (!viewClz) {
            return null
        }
        let result: T = this.viewArr.find(i => i instanceof viewClz) as T
        return result
    }
    /**
     * 移除view
     * @param index 
     * @param mView 
     * @param disableAnim 禁用出场动画
     */
    removeView(index: number, mView: BaseView, disableAnim: boolean) {
        if (mView.disableAnim || disableAnim) {
            mView._close()
        }
        else {
            mView.showBlackBg(false)
            mView.playCloseAnim(() => {
                mView._close()
            })
        }
        this.viewArr.splice(index, 1)

    }

    /**前一个界面继续 */
    resumePreView() {
        for (let i = this.viewArr.length - 1; i >= 0; i--) {
            let mView = this.viewArr[i]
            if (mView.isPaused) {
                mView.resume()
                break
            }
        }

        /**往前查第一个全屏界面之上的历史界面回到前台 */
        for (let i = this.viewArr.length - 1; i >= 0; i--) {
            let mView = this.viewArr[i]
            if (mView.isHide) {
                mView.show()
            }
            if (mView.hidePreViews) {
                break
            }
        }
    }

    /**
     * 关闭屏幕点击
     */
    turnOffScreenClick() {
        if (this.blockNode == null) return
        this.blockNode.active = true
    }

    /**开启屏幕点击 */
    turnOnScreenClick() {
        if (this.blockNode == null) return
        this.blockNode.active = false
    }




    static getParentView(node: cc.Node): BaseView {
        let parent = node.parent
        while (parent) {
            const view = parent.getComponent(BaseView as any)
            if (view) {
                return view as BaseView
            }
            if (parent.parent) {
                parent = parent.parent
            } else {
                parent = null
            }
        }
        return null
    }


    /**
     * 节点是否在游戏视图内
     * @param node 
     * @param containerAABB 区域包围盒
     * @returns 
     */
    isInGameView(node: cc.Node, containerAABB = this.canvasAABB) {
        // return this.canvasBoundingBox.contains(node.worldPosition as any)
        let aabb = node.getBoundingBoxToWorld()
        return aabb.intersects(containerAABB)
    }

    update(dt: number) {
        /**超过一定时间关闭等待阻挡 */
        if (Date.now() - this.lastIncreaseTime > 3000) {
            this.wattingCount = 0
            this.waittingBlockNode.active = false
        }
    }

    wattingCount = 0
    lastIncreaseTime = 0

    /**增加waitting请求数量 */
    increaseWaitingCount() {
        this.lastIncreaseTime = Date.now()
        this.wattingCount++
        if (this.wattingCount == 1 && this.waittingBlockNode) {
            this.waittingBlockNode.active = true
        }
    }

    /**减少waitting请求数量 */
    decreaseWaitingCount() {
        this.wattingCount--
        if (this.wattingCount == 0 && this.waittingBlockNode) {
            this.waittingBlockNode.active = false
        }
    }
}

